﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;

namespace Mesuro.Common {
	public struct Vector {
		/// <summary>
		/// The number of elements in this vector
		/// </summary>
		public readonly uint Size;
		private double[] data;

		/// <summary>
		/// Gets the euclidean magnitude of this vector.
		/// </summary>
		public double Magnitude {
			get {
				double mag = 0;
				foreach (double f in data)
					mag += f * f;
				return (double)Math.Sqrt( mag );
			}
			set {
				double mul = value / Magnitude;
				for (int i = 0; i < Size; i++)
					data[i] *= mul;
			}
		}
		/// <summary>
		/// Get an element of this vector
		/// </summary>
		/// <param seriesKey="i">Which element to retrieve</param>
		/// <returns>Element `i` of the vector</returns>
		public double this[int i] {
			get { return data[i]; }
			set { data[i] = value; }
		}

		/// <summary>
		/// Create a vector of a particular size. All elements will be 0.
		/// </summary>
		/// <param seriesKey="size">The size of this vector.</param>
		public Vector( int size ) {
			this.Size = (uint)size;
			data = new double[size];
		}
		/// <summary>
		/// Create a vector with initial values.
		/// </summary>
		/// <param seriesKey="_data"> the _data to initialise this vector to</param>
		public Vector( params double[] data ) {
			this.Size = (uint)data.Length;
			this.data = new double[Size];
			Array.Copy( data, this.data, Size );
		}

		#region Unary Operators
		public static Vector operator +( Vector a ) {
			return a;
		}
		public static Vector operator -( Vector a ) {
			for (int i = 0; i < a.Size; i++) {
				a[i] = -a[i];
			}
			return a;
		}
		#endregion Unary Operators

		#region Arithmetic Operators
		public static Vector operator +( Vector a, Vector b ) {
			if (a.Size != b.Size) throw new RankException( "Cannot add vectors of unequal size." );
			for (int i = 0; i < a.Size; i++) {
				a[i] += b[i];
			}
			return a;
		}
		public static Vector operator -( Vector a, Vector b ) {
			if (a.Size != b.Size) throw new RankException( "Cannot subtract vectors of unequal size." );
			Vector c = new Vector( (int)a.Size );
			for (int i = 0; i < a.Size; i++) {
				c[i] = a[i] - b[i];
			}
			return c;
		}
		public static double operator *( Vector a, Vector b ) {
			if (a.Size != b.Size) throw new RankException( "Cannot multiply vectors of unequal size." );
			double c = 0;
			for (int i = 0; i < a.Size; i++) {
				c += a[i] * b[i];
			}
			return c;
		}
		public static Vector operator *( Vector a, double b ) {
			for (int i = 0; i < a.Size; i++) {
				a[i] *= b;
			}
			return a;
		}
		public static Vector operator *( double b, Vector a ) {
			for (int i = 0; i < a.Size; i++) {
				a[i] *= b;
			}
			return a;
		}
		public static Vector operator /( Vector a, double b ) {
			for (int i = 0; i < a.Size; i++) {
				a[i] /= b;
			}
			return a;
		}
		#endregion Arithmetic Operators
		#region Piecewise Arithmetic Functions
		public static Vector PiecewiseMultiply( Vector a, Vector b ) {
			if (a.Size != b.Size) throw new RankException( "Cannot (piecewise) multiply vectors of unequal size." );
			for (int i = 0; i < a.Size; i++) {
				a[i] *= b[i];
			}
			return a;
		}
		public static Vector PiecewiseDivide( Vector a, Vector b ) {
			if (a.Size != b.Size) throw new RankException( "Cannot (piecewise) divide vectors of unequal size." );
			for (int i = 0; i < a.Size; i++) {
				a[i] /= b[i];
			}
			return a;
		}
		#endregion Arithmetic Operators

		#region Comparators
		public static bool operator >( Vector a, double b ) {
			return a.Magnitude > b;
		}
		public static bool operator <( Vector a, double b ) {
			return a.Magnitude < b;
		}
		public static bool operator ==( Vector a, double b ) {
			return a.Magnitude == b;
		}
		public static bool operator !=( Vector a, double b ) {
			return a.Magnitude != b;
		}
		public static bool operator ==( double b, Vector a ) {
			return a.Magnitude == b;
		}
		public static bool operator !=( double b, Vector a ) {
			return a.Magnitude != b;
		}
		#endregion Comparators

		#region Implicit Casts
		public static implicit operator Vector( double f ) {
			return new Vector( f );
		}
		public static implicit operator Vector( double[] f ) {
			return new Vector( f );
		}
		public static implicit operator double[]( Vector v ) {
			return v.data;
		}
		#endregion Implicit Casts

		#region Explicit Casts
		public static implicit operator Vector( Point p ) {
			return new Vector( p.X, p.Y );
		}
		public static implicit operator Vector( Size p ) {
			return new Vector( p.Width, p.Height );
		}
		public static explicit operator Vector( Complex p ) {
			return new Vector( p.Re, p.Im );
		}
		public static explicit operator Point( Vector p ) {
			if (p.Size != 2) throw new InvalidCastException( String.Format( "Cannot cast {0} dimensional vector to 2 dimensional point.", p.Size ) );
			return new Point( p[0], p[1] );
		}
		public static explicit operator Size( Vector p ) {
			if (p.Size != 2) throw new InvalidCastException( String.Format( "Cannot cast {0} dimensional vector to 2 dimensional size.", p.Size ) );
			return new Size( p[0], p[1] );
		}
		public static explicit operator Complex( Vector p ) {
			if (p.Size != 2) throw new InvalidCastException( String.Format( "Cannot cast {0} dimensional vector to 2 dimensional complex.", p.Size ) );
			return new Complex( p[0], p[1] );
		}
		#endregion Explicit Casts

		public override bool Equals( object obj ) {
			try {
				Vector v = (Vector)obj;
				if (v.Size != Size) return false;
				for (int i = 0; i < Size; i++)
					if (v[i] != this[i]) return false;
			} catch (Exception) { return false; }
			return true;
		}
		public override int GetHashCode() {
			int ret = 0;
			for (int i = 0; i < Size; i++) {
				ret ^= this[i].GetHashCode();
			}
			return ret;
		}
		public override string ToString() {
			StringBuilder sb = new StringBuilder( "[" );
			sb.Append( data[0] );
			for (int i = 1; i < Size; i++)
				sb.Append( "," ).Append( data[i] );
			sb.Append( "](" ).Append( Size ).Append( ")" );
			return sb.ToString();
		}

		public Vector Top( int nElements ) {
			if (nElements == Size)
				return this;

			Vector v = new Vector( nElements );

			Array.ConstrainedCopy( data, 0, v.data, 0, Math.Min( (int)Size, nElements ) );

			return v;
		}

		public static Vector Concatenate( Vector v1, Vector v2 ) {
			Vector ret = v1.Top( (int)v1.Size + (int)v2.Size );
			Array.Copy( v1.data, ret.data, v1.Size );
			Array.Copy( v2.data, 0, ret.data, v1.Size, v2.Size );
			return ret;
		}
	}

}
